/*
 * @(#)TemplateMethodPlugIn.java $version 2010. 8. 11
 *
 * Copyright 2007-2010 NHN Corp. All rights Reserved. 
 * NHN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nhncorp.usf.macro.config;

import java.util.*;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Node;

import com.nhncorp.lucy.common.plugin.AbstractPlugIn;
import com.nhncorp.lucy.common.plugin.PlugInException;
import com.nhncorp.lucy.common.util.SpringObjectFactory;
import com.nhncorp.lucy.common.util.XMLDOMUtil;

import com.nhncorp.usf.core.result.helper.FreemarkerConfigFactory;
import com.nhncorp.usf.macro.method.CustomMethodLoader;
import com.nhncorp.usf.macro.method.LibraryMethodLoader;
import com.nhncorp.usf.macro.method.MethodInfo;
import com.nhncorp.usf.macro.method.MethodInfoLoader;

import freemarker.template.Configuration;
import freemarker.template.TemplateMethodModel;
import freemarker.template.TemplateModelException;

/**
 * 환경정보를 이용한 {@link TemplateMethodModel} {@link Map} 생성.
 *
 * @author Web Platform Development Team
 */
public class TemplateMethodPlugIn extends AbstractPlugIn {
    Log log = LogFactory.getLog(TemplateMethodPlugIn.class);

    private Map<String, List<MethodInfo>> sortedMethodInfo = new HashMap<String, List<MethodInfo>>();
    private List<MethodInfoLoader> methodLoaders = new ArrayList<MethodInfoLoader>();

    /**
     * 환경파일에 정의된 {@link MethodInfo} 를 이용한 {@link TemplateMethodModel} {@link Entry} 생성.
     * 
     * @throws PlugInException the PlugInException
     */
    @Override
    public void init() throws PlugInException {
        // implement AbstractPlugIn
    }

    /**
     *
     * @param node the Node
     * @return boolean the parsing success
     * @throws PlugInException the PlugInException
     */
    @SuppressWarnings("deprecation")
    @Override
    public boolean build(Node node) throws PlugInException {

        Node methodsNode = XMLDOMUtil.getNode(node, "usf-macro:methods");
        String autoLoad = XMLDOMUtil.getValue(methodsNode, "@autoload");
        log.debug("autoLoad : " + autoLoad);

        if (autoLoad == null || autoLoad.equalsIgnoreCase("true")) {
            methodLoaders.add(new LibraryMethodLoader());
        }

        methodLoaders.add(new CustomMethodLoader());

        try {
            for (MethodInfoLoader methodInfoLoader : methodLoaders) {
                orderByNamespace(methodInfoLoader.load(node));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new PlugInException(e);
        }
        generateMethod();

        return true;
    }

    /**
     * MethodInfo를 이용해 TemplateMethodModel를 생성, 배치
     */
    private void generateMethod() {
        Configuration config = FreemarkerConfigFactory.getConfiguration();

        for (Entry<String, List<MethodInfo>> entry : this.sortedMethodInfo.entrySet()) {
            Map<String, TemplateMethodModel> methods = new HashMap<String, TemplateMethodModel>();

            for (MethodInfo methodInfo : entry.getValue()) {
                TemplateMethodModel templateMethod;
                try {
                    log.debug("TemplateMethodModel generating....");
                    templateMethod = SpringObjectFactory.getInstance().getObject(methodInfo.getClassName());
                    methods.put(methodInfo.getName(), templateMethod);
                } catch (Exception ex) {
                    log.error("TemplateMethodModel generate error...", ex);
                }
            }

            String namespace = entry.getKey();

            log.debug("add template-method namespace : " + namespace);
            try {
                config.setSharedVariable(namespace, methods);
            } catch (TemplateModelException ex) {
                log.error("TemplateModelException...", ex);
            }

        }
    }

    /**
     * method  info를 namespace별로 구분한다
     *
     * @param methodInfos the MethodInfo list
     */
    private void orderByNamespace(List<MethodInfo> methodInfos) {
        for (MethodInfo methodInfo : methodInfos) {
            String namespace = methodInfo.getNamespace();
            List<MethodInfo> list = this.sortedMethodInfo.get(namespace);

            if (list == null) {
                list = new ArrayList<MethodInfo>();
                this.sortedMethodInfo.put(namespace, list);
            }

            list.add(methodInfo);
        }
    }

    /**
     * Gets the method loaders.
     *
     * @return the method loaders
     */
    public Collection<MethodInfoLoader> getMethodLoaders() {
        return methodLoaders;
    }

    /**
     * Gets the method info.
     *
     * @param namespace the namespace
     * @return the method info
     */
    public Collection<MethodInfo> getMethodInfo(String namespace) {
        if (sortedMethodInfo == null || namespace == null) {
            return Collections.emptyList();
        }

        return sortedMethodInfo.get(namespace);
    }
}
